/*==============================================================================
 *	  __  __            _ _         __  __
 *	 |  \/  |          | (_)       |  \/  |
 *	 | \  / | ___   ___| |_  __ _  | \  / | __ _ _ __   __ _  __ _  ___ _ __
 *	 | |\/| |/ _ \ / __| | |/ _` | | |\/| |/ _` | '_ \ / _` |/ _` |/ _ \ '__|
 *	 | |  | | (_) | (__| | | (_| | | |  | | (_| | | | | (_| | (_| |  __/ |
 *	 |_|  |_|\___/ \___|_|_|\__,_| |_|  |_|\__,_|_| |_|\__,_|\__, |\___|_|
 *	  													      __/ |
 *	  													     |___/
 *==============================================================================
 *	Moclia AutoVote for Mirai-api-http by Mirai-Cpp
 *	Copyright (C) 2021 星翛-STASWIT
 *  for Moclia Project & Moclia-Development-Team
 * -----------------------------------------------------------------------------
 *	This program is free software: you can redistribute it and/or modify it
 *	under the terms of the GNU Affero General Public License as published by
 *  the Free Software Foundation, either version 3 of the License, or (at your
 *  option) any later version.
 *
 *	This program is distributed in the hope that it will be useful, but WITHOUT
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
 * FOR A PARTICULAR PURPOSE.
 *	See the GNU Affero General Public License for more details.
 *
 *	You should have received a copy of the GNU Affero General Public License
 * along with this program. If not, see <http://www.gnu.org/licenses/>.
 * -----------------------------------------------------------------------------
 */

#include <iostream>
#include <map>
#include <string>
#include <sstream>
#include <regex>
#include <ctime>
#include <vector>

#include <mirai/mirai.h>
#include <fmt/core.h>
#include <fmt/color.h>
#include <fmt/chrono.h>
#include <fmt/format.h>

#include "GeneralMessage.h"
#include "Configure.h"

using namespace std;
using namespace Cyan;
using namespace std::literals::chrono_literals;
using namespace fmt::literals;

string input;
int CommandNum;
time_t MainTime;
map<GID_t, string> gPermission;
vector<Group_t> groupList;

void GroupPermissionMap()
{
	gPermission.erase(gPermission.begin(), gPermission.end()); // 清空map  
	GID_t groupid; // 群账号  
	string permissionValue; // bot在群里的权限  
	for (INT64 glistSize = 0; glistSize < groupList.size(); glistSize++)
	{
		groupid = groupList[glistSize].GID;
		permissionValue = GroupPermissionStr(groupList[glistSize].Permission);
		gPermission.insert(map<GID_t, string>::value_type(groupid, permissionValue));
	}
}

// 分离式指令系统
void CommandSys()
{
	map<string, int> CommandType = {
		{"/stop",1},
		{"/about",2},
		{"/grouplist",3}
	};
	while (true)
	{
		try
		{
			cin >> input;
			MainTime = std::time(nullptr);
			CommandNum = CommandType[input];
			switch (CommandNum)
			{
				// 结束运行程序
			case 1:
				fmt::print(fg(fmt::color::light_sea_green),
					"[MocliaSystem {:%H:%M:%S} &stop] 已结束进程\n",
					*localtime(&MainTime));
				exit(0);
				break;
				// 控制台帮助
			case 2:
				fmt::print(fg(fmt::color::light_sea_green),
					"[MocliaSystem {:%H:%M:%S} &console] {}\n",
					*localtime(&MainTime), BotHelp());
				break;
			case 3:
				for (int groupListPos = 0; groupListPos < groupList.size(); groupListPos++)
				{
					fmt::print(fg(fmt::color::light_sea_green),
						"[MocliaSystem {:%H:%M:%S} &console] {}\n",
						*localtime(&MainTime), groupList[groupListPos].ToString());
				}
				break;
				// 指令错误回复
			default:
				fmt::print(fg(fmt::color::red),
					"[MocliaSystem {:%H:%M:%S} &err] 指令错误\n",
					*localtime(&MainTime));
				break;
			}
		}
		catch (const std::exception& ex)
		{
			MainTime = std::time(nullptr);
			fmt::print(fg(fmt::color::red),
				"[MocliaSystem {:%H:%M:%S} &err] {}\n",
				*localtime(&MainTime), ex.what()); 
		}

	}
}

int main()
{
#if defined(WIN32) || defined(_WIN32)
	// 切换代码页，让 CMD 可以显示 UTF-8 字符
	system("chcp 65001 && cls");
	// 设置标题
	SetConsoleTitle("MocliaManager");
#endif

	MainTime = std::time(nullptr);
	// 配置文件是否存在检查
	FILE* DBSearch = fopen("./BotConfig.db", "rb");
	if (!DBSearch)
	{
		MocliaAutoVerify::configure();
	}
	string host, auth, ip_addr, port_temp;
	int64_t port, qqNum;

	try
	{
		ip_addr = MocliaAutoVerify::readconfig(3);
		// 正则分离ip地址和端口
		regex extract(("([\\S\\s]*):([0-9]*)"),
			regex_constants::ECMAScript | regex_constants::icase);
		smatch result;
		regex_match(ip_addr, result, extract);
		host = result[1];
		port_temp = result[2];
		port = atoi(port_temp.c_str());
		// 机器人QQ号
		qqNum = atoi(MocliaAutoVerify::readconfig(1).c_str());
		// 注册密钥
		auth = MocliaAutoVerify::readconfig(2);
	}
	catch (const std::exception& ex)
	{
		MainTime = std::time(nullptr);
		fmt::print(fg(fmt::color::red),
			"[MocliaSystem {:%H:%M:%S} &err] {}\n",
			*localtime(&MainTime), ex.what());
	}

	QQ_t qqNum_qq = QQ_t(qqNum);

	MiraiBot bot(host, port);
	while (true)
	{
		try
		{
			bot.Auth(auth, qqNum_qq);
			break;
		}
		catch (const std::exception& ex)
		{
			MainTime = std::time(nullptr);
			fmt::print(fg(fmt::color::red),
				"[MocliaSystem {:%H:%M:%S} &err] {}\n",
				*localtime(&MainTime), ex.what());
		}
		MiraiBot::SleepSeconds(1);
	}
	
	groupList.swap(bot.GetGroupList());
	GroupPermissionMap();

	fmt::print(fg(fmt::color::light_sky_blue),
		"{}\n",
		R"(
==============================================================================
    __  __            _ _         __  __                                   
   |  \/  |          | (_)       |  \/  |                                  
   | \  / | ___   ___| |_  __ _  | \  / | __ _ _ __   __ _  __ _  ___ _ __ 
   | |\/| |/ _ \ / __| | |/ _` | | |\/| |/ _` | '_ \ / _` |/ _` |/ _ \ '__|
   | |  | | (_) | (__| | | (_| | | |  | | (_| | | | | (_| | (_| |  __/ |   
   |_|  |_|\___/ \___|_|_|\__,_| |_|  |_|\__,_|_| |_|\__,_|\__, |\___|_|   
                                                            __/ |          
                                                           |___/           
 =============================================================================)"
	);

	fmt::print(fg(fmt::color::aqua),
		"[MocliaSystem {:%H:%M:%S} &startup] MocliaAutoVote正在运行中……\n",
		*localtime(&MainTime));

	fmt::print(fg(fmt::color::aqua),
		"[MocliaSystem {:%H:%M:%S} &startup] MocliaAutoVote版本：1.0.0R\n",
		*localtime(&MainTime));
	fmt::print(fg(fmt::color::aqua),
		"[MocliaSystem {:%H:%M:%S} &startup] Mirai-API-Http版本：{}\n",
		*localtime(&MainTime), bot.GetMiraiApiHttpVersion());
	fmt::print(fg(fmt::color::aqua),
		"[MocliaSystem {:%H:%M:%S} &startup] Mirai-Cpp版本：{}\n",
		*localtime(&MainTime), bot.GetMiraiCppVersion());

	thread CommandSystem(CommandSys);
	CommandSystem.detach();

	/*群事件部分*/
	/*机器人被邀请加入群聊*/
	bot.On<BotInvitedJoinGroupRequestEvent>(
		[&](BotInvitedJoinGroupRequestEvent ge)
		{
			try
			{
				MainTime = std::time(nullptr);
				fmt::print(fg(fmt::color::spring_green),
					"[MocliaSystem {:%H:%M:%S} &group] 【{}】({})邀请入群【{}】({})，已同意\n"
					, *localtime(&MainTime),
					ge.Nick, ge.GroupName, ge.GroupId.ToInt64());
				ge.Accept();

				groupList.swap(bot.GetGroupList());
				GroupPermissionMap();
			}
			catch (const std::exception& ex)
			{
				MainTime = std::time(nullptr);
				fmt::print(fg(fmt::color::red),
					"[MocliaSystem {:%H:%M:%S} &err] {}\n",
					*localtime(&MainTime), ex.what());
			}
		});

	/*机器人加入新群*/
	bot.On<BotJoinGroupEvent>(
		[&](BotJoinGroupEvent jge)
		{
			try
			{
				MainTime = std::time(nullptr);
				fmt::print(fg(fmt::color::spring_green),
					"[MocliaSystem {:%H:%M:%S} &group] 已加入群【{}】({})\n"
					, *localtime(&MainTime),
					jge.Group.Name, jge.Group.GID.ToInt64());

				groupList.swap(bot.GetGroupList());
				GroupPermissionMap();
			}
			catch (const std::exception& ex)
			{
				MainTime = std::time(nullptr);
				fmt::print(fg(fmt::color::red),
					"[MocliaSystem {:%H:%M:%S} &err] {}\n",
					*localtime(&MainTime), ex.what());
			}
		});

	/*机器人主动退群*/
	bot.On<BotLeaveEventActive>(
		[&](BotLeaveEventActive lea)
		{
			try
			{
				MainTime = std::time(nullptr);
				fmt::print(fg(fmt::color::spring_green),
					"[MocliaSystem {:%H:%M:%S} &group] 已主动退出群【{}】({})\n"
					, *localtime(&MainTime),
					lea.Group.Name, lea.Group.GID.ToInt64());

				groupList.swap(bot.GetGroupList());
				GroupPermissionMap();
			}
			catch (const std::exception& ex)
			{
				MainTime = std::time(nullptr);
				fmt::print(fg(fmt::color::red),
					"[MocliaSystem {:%H:%M:%S} &err] {}\n",
					*localtime(&MainTime), ex.what());
			}
		});

	/*机器人被踢出群聊*/
	bot.On<BotLeaveEventKick>(
		[&](BotLeaveEventKick lek)
		{
			try
			{
				MainTime = std::time(nullptr);
				fmt::print(fg(fmt::color::spring_green),
					"[MocliaSystem {:%H:%M:%S} &group] 被踢出群【{}】({})\n"
					, *localtime(&MainTime),
					lek.Group.Name, lek.Group.GID.ToInt64());

				groupList.swap(bot.GetGroupList());
				GroupPermissionMap();
			}
			catch (const std::exception& ex)
			{
				MainTime = std::time(nullptr);
				fmt::print(fg(fmt::color::red),
					"[MocliaSystem {:%H:%M:%S} &err] {}\n",
					*localtime(&MainTime), ex.what());
			}
		});

	/*机器人缓存信息更新部分*/

	/*群管部分*/
	bot.On<GroupMessage>(
		[&](GroupMessage gm)
		{
			try
			{
				string plain = gm.MessageChain.GetPlainText();
				string specialTitle;
				GID_t group;
				QQ_t sender;
				
				if (plain.find("*设置头衔") == 0)
				{
					group = gm.Sender.Group.GID;
					if (gPermission[group] == "OWNER")
					{
						specialTitle = plain.substr(strlen("*设置头衔"));
						sender = gm.Sender.QQ;
						bot.SetGroupMemberSpecialTitle(group, sender, specialTitle);
						gm.Reply(MessageChain().Plain("设置成功！"));

						MainTime = std::time(nullptr);
						fmt::print(fg(fmt::color::spring_green),
							"[MocliaSystem {:%H:%M:%S} &group] 【{}】({})在群【{}】({})设置头衔为：【{}】\n"
							, *localtime(&MainTime),
							gm.Sender.MemberName, sender.ToInt64(), gm.Sender.Group.Name, group.ToInt64(), specialTitle);

						return;
					}
					else
					{
						gm.Reply(MessageChain().Plain("只有群主才有权限修改头衔哟"));

						MainTime = std::time(nullptr);
						fmt::print(fg(fmt::color::fire_brick),
							"[MocliaSystem {:%H:%M:%S} &group] 群【{}】({})尝试设置头衔失败，理由：机器人非群主\n"
							, *localtime(&MainTime),
							gm.Sender.Group.Name, group.ToInt64());

						return;
					}
					
				}
			}
			catch (const std::exception& ex)
			{
				MainTime = std::time(nullptr);
				fmt::print(fg(fmt::color::red),
					"[MocliaSystem {:%H:%M:%S} &err] {}\n",
					*localtime(&MainTime), ex.what());
			}
		});

	/*机器人被禁言*/
	bot.On<BotMuteEvent>(
		[&](BotMuteEvent bme)
		{
			try
			{
				MainTime = std::time(nullptr);
				fmt::print(fg(fmt::color::spring_green),
					"[MocliaSystem {:%H:%M:%S} &group] 在群【{}】({})被【{}】({})禁言{}秒\n"
					, *localtime(&MainTime),
					bme.Operator.Group.Name, bme.Operator.Group.GID.ToInt64(), bme.Operator.MemberName, bme.Operator.QQ.ToInt64(), bme.DurationSeconds);
			}
			catch (const std::exception& ex)
			{
				MainTime = std::time(nullptr);
				fmt::print(fg(fmt::color::red),
					"[MocliaSystem {:%H:%M:%S} &err] {}\n",
					*localtime(&MainTime), ex.what());
			}
		});

	/*机器人被取消禁言*/
	bot.On<BotUnmuteEvent>(
		[&](BotUnmuteEvent bue)
		{
			try
			{
				MainTime = std::time(nullptr);
				fmt::print(fg(fmt::color::spring_green),
					"[MocliaSystem {:%H:%M:%S} &group] 已在群【{}】({})被取消禁言\n"
					, *localtime(&MainTime),
					bue.Operator.Group.Name, bue.Operator.Group.GID.ToInt64());
			}
			catch (const std::exception& ex)
			{
				MainTime = std::time(nullptr);
				fmt::print(fg(fmt::color::red),
					"[MocliaSystem {:%H:%M:%S} &err] {}\n",
					*localtime(&MainTime), ex.what());
			}
		});
	/*私聊事件部分*/

	/*新好友申请*/
	bot.On<NewFriendRequestEvent>(
		[&](NewFriendRequestEvent fe)
		{
			try
			{
				MainTime = std::time(nullptr);
				fmt::print(fg(fmt::color::spring_green),
					"[MocliaSystem {:%H:%M:%S} &group] 【{}】({})申请加好友，已同意\n"
					, *localtime(&MainTime),
					fe.Nick, fe.FromId.ToInt64());
				fe.Accept();
			}
			catch (const std::exception& ex)
			{
				MainTime = std::time(nullptr);
				fmt::print(fg(fmt::color::red),
					"[MocliaSystem {:%H:%M:%S} &err] {}\n",
					*localtime(&MainTime), ex.what());
			}
		});

	/*机器人事件部分*/
	/*机器人掉线*/
	/*机器人重新上线*/

	bot.EventLoop();

	return 0;
}